package udpsubpub

import (
	"bytes"
	"fmt"
	"gitee.com/ymofen/gobase"
	"gitee.com/ymofen/gobase/subpub"
	"gitee.com/ymofen/kplbase/core/knet"
	"net"
	"sync/atomic"
	"time"
)

type UdpServerNode struct {
	sessionSN   int32
	connectcnt  int32
	startedflag int32
	network     string
	addr        string
	timestamp   string
	maxonline   int
	serv        *knet.NetUdpListener
	ex          *subpub.Subchannel
}

func (this *UdpServerNode) checkStart() error {
	if !atomic.CompareAndSwapInt32(&this.startedflag, 0, 1) {
		return nil
	}
	serv := knet.NewNetUdpListener()
	serv.Config(this.network, this.addr)
	serv.OnErr = func(err error) {
		this.ex.Pub("evt", 0, subpub.ACTION_ERROR, err)
	}
	serv.HandleConnFunc = func(conn *net.UDPConn) {
		go this.innerHandleConnFunc(conn)
	}
	this.serv = serv

	serv.CheckStart()
	return nil
}

func (this *UdpServerNode) Close() error {
	serv := this.serv
	if serv != nil {
		serv.Close()
	}
	this.ex.Pub("notify-closed", 0, "请求关闭")
	return nil
}

func (this *UdpServerNode) PubBuf(max int, data []byte) int {
	return this.ex.Pub("send", max, data)
}

func (this *UdpServerNode) innerHandleConnFunc(conn *net.UDPConn) {
	if gobase.GoFunCatchException {
		defer gobase.DeferCatchPanic()
	}

	sessionid := fmt.Sprintf("%d-%s", atomic.AddInt32(&this.sessionSN, 1), gobase.NowIDString())
	var closedflag int32 = 0

	var reason string = ""

	for {
		buf := make([]byte, 4096)
		n, _, e1 := conn.ReadFromUDP(buf)
		if e1 != nil {
			if atomic.CompareAndSwapInt32(&closedflag, 0, 1) {
				reason = e1.Error()
			}
			break
		}
		if this.timestamp == "1" {
			currentTime := time.Now().UnixMicro()
			// 将时间戳转换为 []byte
			hexBytes := make([]byte, 8) // 64位整数 Unix 时间戳通常是8个字节
			for i := 0; i < 8; i++ {
				hexBytes[7-i] = byte(currentTime >> (i * 8) & 0xFF) // 从高位到低位将每个字节存入
			}
			this.ex.Pub("evt", 0, subpub.ACTION_SERVER_CONN_RECV_BUF, sessionid, bytes.Join([][]byte{hexBytes, buf[:n]}, []byte{}))

		} else {
			this.ex.Pub("evt", 0, subpub.ACTION_SERVER_CONN_RECV_BUF, sessionid, buf[:n])
		}

	}
	atomic.StoreInt32(&closedflag, 1)
	this.ex.Pub("evt", 0, subpub.ACTION_SERVER_CONN_DISCONNECTED, sessionid, reason)
}

var (
	DefaultTcpServerGroup = gobase.NewConnectObjectRefStorage(newUdpServerNode)
)

func newUdpServerNode(connstr string) interface{} {
	conf := gobase.NewStrMap()
	conf.URLFormDecode(connstr)
	node := &UdpServerNode{network: conf.StringByName("network", "udp"),
		addr:      conf.StringByName("addr", ""),
		timestamp: conf.StringByName("timestamp", "0")}
	node.ex = subpub.NewSubchannel()
	return node
}
